package com.xqboss.apps.service.consumer;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.cloudauth20190307.models.DescribeFaceVerifyResponse;
import com.aliyun.cloudauth20190307.models.InitFaceVerifyResponseBody;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.constant.LockKey;
import com.xqboss.apps.constant.RedisKey;
import com.xqboss.apps.domain.consumer.ConsumerRealName;
import com.xqboss.apps.dto.consumer.WebConsumerRealNameDto;
import com.xqboss.apps.dto.sys.SysUserDto;
import com.xqboss.apps.enums.user.RealAuthStatusEnum;
import com.xqboss.apps.mapper.consumer.ConsumerRealNameMapper;
import com.xqboss.apps.service.aliyun.AliFaceService;
import com.xqboss.apps.service.hama.HamaHelpService;
import com.xqboss.apps.vo.consumer.WebConsumerRealNameVo;
import com.xqboss.apps.vo.sys.SysUserVo;
import com.xqboss.apps.vo.sys.WebSysUserInfoVo;
import com.xqboss.apps.vo.user.UserRealNameStatusVo;
import com.xqboss.apps.vo.user.WithdrawRealNameVo;
import com.xqboss.common.constant.CacheConstants;
import com.xqboss.common.core.domain.R;
import com.xqboss.common.core.domain.entity.SysDictData;
import com.xqboss.common.core.domain.entity.SysUser;
import com.xqboss.common.core.redis.RedisCache;
import com.xqboss.common.core.redis.lock.LockValue;
import com.xqboss.common.core.redis.lock.RedisLock;
import com.xqboss.common.core.text.StrFormatter;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.AliIdentityUtil;
import com.xqboss.common.utils.DateUtils;
import com.xqboss.common.utils.LocalDateUtils;
import com.xqboss.common.utils.uuid.IdUtils;
import com.xqboss.system.mapper.SysDictDataMapper;
import com.xqboss.system.mapper.SysUserMapper;
import com.xqboss.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户实名认证Service业务层处理
 *
 * @author xqboss
 * @date 2023-08-08
 */
@Service
@Slf4j
public class ConsumerRealNameService extends ServiceImpl<ConsumerRealNameMapper, ConsumerRealName> {

    @Resource
    private ConsumerRealNameMapper consumerRealNameMapper;
    @Value("${authentication.host}")
    private String host;
    @Value("${authentication.appcode}")
    private String appcode;
    @Value("${authentication.idCardFrontPath}")
    private String idCardFrontPath;
    @Value("${authentication.ageLimit}")
    private int ageLimit;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Resource
    private SysDictDataMapper sysDictDataMapper;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private AliFaceService aliFaceService;

    @Autowired
    private HamaHelpService hamaHelpService;


    /**
     * 批量删除用户实名认证
     *
     * @param ids 需要删除的用户实名认证主键
     * @return 结果
     */

    public int deleteConsumerRealNameByIds(Long[] ids) {
        return consumerRealNameMapper.deleteConsumerRealNameByIds(ids);
    }

    /**
     * 用户实人认证
     *
     * @param idCardName
     * @param idCard
     * @param userId
     * @param metaInfo
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_REAL_NAME)
    public UserRealNameStatusVo realName(String idCardName, @LockValue String idCard, Long userId, String metaInfo, String returnUrl) {
        // 优先检测实名状态
        ConsumerRealName realName = getOne(new LambdaQueryWrapper<ConsumerRealName>()
                .eq(ConsumerRealName::getUserId, userId)
                .orderByDesc(ConsumerRealName::getId)
                .last("LIMIT 1"));
        if(realName != null && realName.getAuthStatus() == RealAuthStatusEnum.AUTH_SUCCESS){
            throw new ServiceException("当前账号已存在实名认证信息！");
        }
        if (!IdcardUtil.isValidCard(idCard)) {
            throw new ServiceException("身份证无效");
        }
        if (IdcardUtil.getAgeByIdCard(idCard) < ageLimit) {
            throw new ServiceException("未满18周岁禁止平台交易");
        }
        // 获取该身份证平台是否已经有实名认证信息，如果有可以直接用于参考
        ConsumerRealName realInfo = this.getOne(new LambdaQueryWrapper<ConsumerRealName>()
                .eq(ConsumerRealName::getIdCard, idCard)
                .last("LIMIT 1"));

        if(Objects.nonNull(realInfo)){
            //验证身份证号码与身份证号码是否匹配
            if(!realInfo.getIdCardName().equals(idCardName)){
                throw new ServiceException("姓名和身份证号不匹配");
            }

            // 因为平台已经有该身份信息，则还需要判断是否超过3次
            long count = this.count(new LambdaQueryWrapper<ConsumerRealName>()
                    .eq(ConsumerRealName::getIdCard, idCard)
                    .eq(ConsumerRealName::getAuthStatus, RealAuthStatusEnum.AUTH_SUCCESS));
            Integer maxAuthCount = sysConfigService.getValue(Config.Sys.Auth.身份证实名最大账户数量);
            if (count >= maxAuthCount) {
                throw new ServiceException(StrFormatter.format("一个身份证最多只能认证{}个账号", maxAuthCount));
            }
        }else{
            //验证身份证号码与身份证号码是否匹配
            Boolean isOk = AliIdentityUtil.cardNoAndRealName(appcode, idCard, idCardName);
            if (ObjUtil.isNull(isOk) || !isOk) {
                throw new ServiceException("姓名和身份证号不匹配");
            }
        }

        // 身份证验证通过
        // 判断是否开启人脸检测
        ConsumerRealName consumerRealName = new ConsumerRealName();
        if(sysConfigService.getValue(Config.Sys.Auth.是否开启人脸认证)){
            // 生成人脸检测请求ID
            consumerRealName.setUserId(userId);
            consumerRealName.setIdCardName(idCardName);
            consumerRealName.setIdCard(idCard);
            consumerRealName.setAuthStatus(RealAuthStatusEnum.WAIT_FACE);
            consumerRealName.setAuthTime(LocalDateTime.now());
            if(realName != null){
                consumerRealName.setId(realName.getId());
                updateById(consumerRealName);
            }else{
                save(consumerRealName);
            }

            String token = IdUtils.fastSimpleUUID();
            InitFaceVerifyResponseBody response = aliFaceService.initFaceVerify(String.valueOf(consumerRealName.getId()), consumerRealName.getIdCardName(), consumerRealName.getIdCard(), metaInfo, returnUrl, token);
            if("200".equals(response.getCode())){
                consumerRealName.setAliFaceCertifyId(response.getResultObject().getCertifyId());
                consumerRealName.setAliFaceCertifyUrl(response.getResultObject().getCertifyUrl());
                updateById(consumerRealName);
                redisCache.setCacheObject(RedisKey.KEY_ALI_FACE_TOKEN + token, userId, 30, TimeUnit.MINUTES);
            }else{
                log.error("阿里人脸识别失败：{}" , JSONObject.toJSONString(response));
                consumerRealName.setAuthStatus(RealAuthStatusEnum.AUTH_FAILED);
                updateById(consumerRealName);
                switch (response.getCode()){
                    case "400": //400 参数不能为空
                    case "401": //401 参数非法
                    case "402": //402 应用配置不存在
                    case "404": //404 认证场景配置不存在
                    case "410": //410 未开通服务
                    case "411": //411 RAM无权限
                    case "412": //412 欠费中
                        throw new ServiceException("实名认证失败，请联系客服！");
                    case "414": //414 设备类型不支持
                    case "415": //415 SDK版本不支持
                    case "416": //416 系统版本不支持
                        throw new ServiceException("当前设备不支持实名认证，请尝试升级或者更换设备！");
                    case "418": //418 刷脸失败次数过多
                        throw new ServiceException("今日认证失败次数已达上限！");
                    default:
                        throw new ServiceException("实名认证失败！");
                }
            }
        }else{
            consumerRealName.setUserId(userId);
            consumerRealName.setIdCardName(idCardName);
            consumerRealName.setIdCard(idCard);
            // 判断是否是首次认证
            long count = count(new LambdaQueryWrapper<ConsumerRealName>()
                    .eq(ConsumerRealName::getIdCard, idCard)
                    .eq(ConsumerRealName::getAuthStatus, RealAuthStatusEnum.AUTH_SUCCESS));
            consumerRealName.setIsFirstReal(count == 0);
            consumerRealName.setAuthStatus(RealAuthStatusEnum.AUTH_SUCCESS);
            consumerRealName.setAuthTime(LocalDateTime.now());
            if(realName != null){
                consumerRealName.setId(realName.getId());
                updateById(consumerRealName);
            }else{
                save(consumerRealName);
            }
        }

        UserRealNameStatusVo vo = new UserRealNameStatusVo();
        vo.setStatus(consumerRealName.getAuthStatus());
        vo.setCertifyId(consumerRealName.getAliFaceCertifyId());
        vo.setCertifyUrl(consumerRealName.getAliFaceCertifyUrl());
        vo.setIdCard(consumerRealName.getIdCard());
        vo.setIdCardName(consumerRealName.getIdCardName());

        // 认证成功，判断是否为运动场队长邀请
        if (consumerRealName.getAuthStatus() == RealAuthStatusEnum.AUTH_SUCCESS) {
            hamaHelpService.startProductionAfterRealName(userId);
        }
        return vo;
    }

    /**
     * 同步用户实名状态
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_REAL_NAME)
    public UserRealNameStatusVo syncUserRealNameStatus(Long userId){
        UserRealNameStatusVo vo = new UserRealNameStatusVo();
        // 获取用户实名记录
        ConsumerRealName realName = getOne(new LambdaQueryWrapper<ConsumerRealName>()
                .eq(ConsumerRealName::getUserId, userId)
                .orderByDesc(ConsumerRealName::getAuthTime)
                .last("LIMIT 1"));

        if(Objects.isNull(realName)){
            throw new ServiceException("用户暂无实名信息！");
        }

        if(realName.getAuthStatus() == RealAuthStatusEnum.WAIT_FACE){
            // 同步查询阿里认证状态
            try{
                DescribeFaceVerifyResponse response = aliFaceService.getFaceVerifyStatus(realName.getAliFaceCertifyId());
                if(response.getBody() != null){
                    if("200".equals(response.getBody().getCode())){
                        if("T".equals(response.getBody().getResultObject().passed)){
                            // 认证成功, 更新认证状态
                            // 判断是否是首次认证
                            long count = count(new LambdaQueryWrapper<ConsumerRealName>()
                                    .eq(ConsumerRealName::getIdCard, realName.getIdCard())
                                    .eq(ConsumerRealName::getAuthStatus, RealAuthStatusEnum.AUTH_SUCCESS));
                            realName.setIsFirstReal(count == 0);
                            if(count > sysConfigService.getValue(Config.Sys.Auth.身份证实名最大账户数量)){
                                // 超过最大认证次数时，置为认证失败！
                                realName.setAuthStatus(RealAuthStatusEnum.AUTH_FAILED);
                            }else{
                                realName.setAuthStatus(RealAuthStatusEnum.AUTH_SUCCESS);
                            }
                        }else{
                            realName.setAuthStatus(RealAuthStatusEnum.AUTH_FAILED);
                        }
                    }else{
                        realName.setAuthStatus(RealAuthStatusEnum.AUTH_FAILED);
                    }
                    realName.setAuthTime(LocalDateTime.now());
                    updateById(realName);
                }else{
                    throw new ServiceException("获取认证状态失败！");
                }
            }catch (ServiceException e){
                throw e;
            }catch (Exception e){
                log.error("获取人脸认证异常", e);
                throw new ServiceException("获取认证状态失败！");
            }
        }
        vo.setStatus(realName.getAuthStatus());
        vo.setIdCard(realName.getIdCard());
        vo.setIdCardName(realName.getIdCardName());

        // 认证成功，判断是否为运动场队长邀请
        if (vo.getStatus() == RealAuthStatusEnum.AUTH_SUCCESS) {
            hamaHelpService.startProductionAfterRealName(userId);
        }
        return vo;
    }

    /**
     * 获取用户实名信息
     *
     * @param userId
     * @return
     */
    public ConsumerRealName getUserRealNameOrNull(Long userId) {
        List<ConsumerRealName> list = list(new LambdaQueryWrapper<ConsumerRealName>()
                .eq(ConsumerRealName::getUserId, userId)
                .eq(ConsumerRealName::getAuthStatus, RealAuthStatusEnum.AUTH_SUCCESS));
        return list.stream().findFirst().orElse(null);
    }

    /**
     * 获取用户实名信息
     *
     * @param userId
     * @return
     */
    public ConsumerRealName requireRealName(Long userId) {
        ConsumerRealName realName = getUserRealNameOrNull(userId);
        if (Objects.isNull(realName)) {
            throw new ServiceException("您还未实名认证！");
        }
        return realName;
    }

    public R authentication(String idCardName, String idCard, Long userId) {
        ConsumerRealName realName = requireRealName(userId);
        if (!StrUtil.equals(realName.getIdCardName(), idCardName)
                || !StrUtil.equals(realName.getIdCard(), idCard)) {
            return R.fail("认证失败请重试");
        }
        // 保存验证码信息
        String code = IdUtils.simpleUUID();
        String verifyKey = CacheConstants.AUTHENTICATION_KEY + code;
        redisCache.setCacheObject(verifyKey, code, 5, TimeUnit.MINUTES);
        return R.ok(code);
    }

    public List<SysUserVo> findBy(SysUserDto dto) {
        return consumerRealNameMapper.findBy(dto);
    }

    public R<WebSysUserInfoVo> findByKey(Long userId) {
        SysUser user = sysUserMapper.selectUserById(userId);
        Objects.requireNonNull(user, "用户不存在");
        WebSysUserInfoVo vo = consumerRealNameMapper.findByKey(userId);
        String str = LocalDateUtils.formatDateTimeToStr(vo.getCreateTime(), "yyyy-MM-dd");
        try {
            vo.setRegisterDay(LocalDateUtils.getDifferentDays(str, DateUtils.getDate()));
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        if (StrUtil.isNotBlank(vo.getHobbies())) {
            List<String> hobbies = StrSplitter.split(vo.getHobbies(), ",", 0, true, true);
            LambdaQueryWrapper<SysDictData> dictWq = new LambdaQueryWrapper<>();
            dictWq.in(SysDictData::getDictCode, hobbies);
            List<SysDictData> typeList = sysDictDataMapper.selectList(dictWq);
            vo.setHobbies(typeList.stream().map(SysDictData::getDictLabel).collect(Collectors.joining(",")));
        }
        return R.ok(vo);
    }

    public List<WebConsumerRealNameVo> selectConsumerRealNameList(WebConsumerRealNameDto dto) {
        return consumerRealNameMapper.selectConsumerRealNameList(dto);
    }

    /**
     * 根据用户id删除用户实名信息
     * @param userId
     * @return
     */
    public boolean deleteByUserId(Long userId) {
        LambdaQueryWrapper<ConsumerRealName> wp = new LambdaQueryWrapper<>();
        wp.eq(ConsumerRealName::getUserId, userId);
        return this.remove(wp);
    }

    /**
     * 获取用户实名信息
     *
     * @param userId
     * @return
     */
    public WithdrawRealNameVo getRealName(Long userId) {
        ConsumerRealName one = this.lambdaQuery().eq(ConsumerRealName::getUserId, userId).eq(ConsumerRealName::getAuthStatus, 1).one();
        return BeanUtil.copyProperties(one, WithdrawRealNameVo.class);
    }
}
